package com.you.raft.core.task;

import com.you.raft.common.entity.*;
import com.you.raft.core.impl.DefaultRaftNode;
import com.you.raft.rpc.RpcRaftClient;
import com.you.raft.common.entity.Request;
import com.you.raft.thread.RaftThreadPool;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class HeartBeatTask implements Runnable {
    private static final long heartBeatTick = 1000;
    private DefaultRaftNode node;
    private Cluster cluster;
    private RpcRaftClient rpcClient;

    public HeartBeatTask(DefaultRaftNode node) {
        this.node = node;
        this.cluster = node.getCluster();
        this.rpcClient = node.getRpcClient();
    }

    @Override
    public void run() {
        if (node.getStatus() != NodeStatus.LEADER) {
            return;
        }
        long current = System.currentTimeMillis();
        if (current - node.getPreHeartBeatTime() < heartBeatTick) {
            return;
        }
        for (Node cnode : cluster.getOtherNodes()) {
            log.info("节点 {} nextIndex={}", cnode.getAddress(), node.getNextIndexs().get(cnode));
        }
        node.setPreHeartBeatTime(System.currentTimeMillis());
        for (Node peer : cluster.getOtherNodes()) {
            // 心跳时基本信息:领导者已提交的日志号(不需要日志项，已经前一个日志的日志号和任期的信息
            // 心跳的目的：
            // 一是让FOLLOWER节点知道LEADER正在正确运行
            // 二是告知FOLLOWER节点LEADER已经提交的日志号,以便让FOLLOWER节点应用提交了的日志
            // @可以在一致性模块Consensus的附加日志请求appendEntry,查看FOLLOWER节点对于心跳的处理
            AppendEntryRequest param = AppendEntryRequest.builder()
                    .entries(null)
                    .leaderId(cluster.getSelf().getAddress())
                    .term(node.getCurrentTerm())
                    .leaderCommit(node.getCommitIndex())
                    .prevLogIndex(-1).preLogTerm(-1).build();

            Request request = new Request(
                    Request.APPEND_ENTRIES,
                    peer.getAddress(),
                    param
            );

            RaftThreadPool.execute(() -> {
                try {
                    AppendEntryResponse entryResponse = rpcClient.send(request);
                    long term = entryResponse.getTerm();
                    if (term > node.getCurrentTerm()) {
                        log.error("节点收到的附加日志响应任期{},大于当前任期{}", term, node.getCurrentTerm());
                        node.setCurrentTerm(term);
                        node.setVotedFor("");
                        node.setStatus(NodeStatus.FOLLOWER);
                    }
                } catch (Exception e) {
                    log.error("心跳响应异常, 请求的地址 : {} ", request.getAddr());
                }
            }, false);
        }
    }
}
